import argparse
import cv2
import imutils
import numpy as np
import os

np.set_printoptions(suppress=True)


def order_points(pts):
    # 进行初始化点的位置，左上、右上、左下、右下
    rect = np.zeros((4, 2), dtype="float32")
    # 采取四个点的x+y的和，以最小的和为左上的点，x+y最大为右下的点
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[3] = pts[np.argmax(s)]
    # 定义一左下点和右上点的位置，用|x-y|表示，小的为右上，大的为左下
    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[2] = pts[np.argmax(diff)]

    return rect


def four_point_transform(image, pts):
    rect = order_points(pts)
    print(rect)
    # 目标的像素值大小
    width = image.shape[1]  # 图片的宽
    height = image.shape[0]  # 图片的高
    # 设置目标画布的大小
    canvasPoints = np.array([[0, 0], [width, 0], [0, height], [width, height]])
    canvasPoints = np.float32(canvasPoints)
    print(canvasPoints)
    # 计算转换矩阵
    perspectiveMatrix = cv2.getPerspectiveTransform(rect, canvasPoints)
    print(perspectiveMatrix)
    # 完成透视变换
    perspectiveImg = cv2.warpPerspective(image, perspectiveMatrix, (width, height))
    return perspectiveImg


def four_point_get(stitched):
    # 将拼接图像转换为灰度
    gray = cv2.cvtColor(stitched, cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray, (3, 3), 0)
    # 对灰度图像进行阈值二值化，
    # 这样所有大于零的像素都设置为255（前景），而其他所有像素都保持为0（背景）
    thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)[1]  # 返回值为retval, dst
    if is_show:
        cv2.namedWindow('thresh', cv2.WINDOW_KEEPRATIO)
        cv2.imshow('thresh', thresh)
        cv2.waitKey(0)
    # 在阈值图像中找到所有外部轮廓，然后找到 “最大 ”轮廓，它将是拼接图像的轮廓
    # cv2.RETR_EXTERNAL：只找外轮廓。cv2.CHAIN_APPROX_SIMPLE：输出少量轮廓点
    # 输出参数1：图像
    # 输出参数2：轮廓列表
    # 输出参数3：层级
    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    print(cnts[0])  # contours:返回的轮廓, hierarchy:每条轮廓对应的属性
    print(len(cnts))
    # 为了不区分opencv2或opencv3，后面好运算
    cnts = imutils.grab_contours(cnts)
    # 这一步是进行轮廓所围成的面积，并且进行从大到小的排列
    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
    # 第三个参数指定绘制轮廓list中的哪条轮廓，如果是-1，则绘制其中的所有轮廓。后面的参数很简单。
    # 其中color：线的颜色（0，0，255）表示红色；（255，0，0）表示蓝色；thickness表明轮廓线的宽度，如果是-1（cv2.FILLED），则为填充模式。
    # cv2.drawContours(stitched, cnts, -1, (0, 0, 255), 3)
    # cv2.namedWindow('Contours', cv2.WINDOW_KEEPRATIO)
    # cv2.imshow("Contours", stitched)
    # cv2.waitKey(0)
    screenCnt = None
    for c in cnts:

        # 获取轮廓的周长
        peri = cv2.arcLength(c, True)
        # 用该函数进行图形的相似
        approx = cv2.approxPolyDP(c, 0.02 * peri, True)
        # 如果我们相似的轮廓是四个点表示的，那么我们就可以看作这是轮廓点就是我们所要的
        if len(approx) == 4:
            screenCnt = approx
            break
        print(screenCnt)

    # 画出轮廓
    if screenCnt is None:
        screenCnt = np.array([[[0,0]],[[0,stitched.shape[1]]],[[stitched.shape[0],stitched.shape[1]]],[[stitched.shape[0],0]]],dtype=np.int32)
    cv2.drawContours(stitched, [screenCnt], -1, (0, 255, 255), 2)
    if is_show:
        cv2.namedWindow('Outline', cv2.WINDOW_KEEPRATIO)
        cv2.imshow("Outline", stitched)
        cv2.waitKey(0)
    ratio = 1  # image.shape[0] / 500
    # print("ratio:",ratio)
    pts = screenCnt.reshape(4, 2) * ratio
    return pts


# 构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", default='image', type=str, required=True,
                help="path to stitched image to correct")
ap.add_argument("-o", "--output", default='output.jpg', type=str, required=False,
                help="path to the output image")
ap.add_argument("-n", "--noshow", action="store_false", required=False,
                help="is to show tmp and result images ")
args = vars(ap.parse_args())  # vars函数是实现返回对象object的属性和属性值的字典对象

print("[INFO] loading image...")
src_FILE = args["image"]
stitched = cv2.imread(src_FILE)
SHRT_MAX = 32767 - 1
parts = []
is_show = args["noshow"]

if stitched.shape[0] > SHRT_MAX or stitched.shape[1] > SHRT_MAX:
    print("[ERROR] image size biger than 32767...")
    # exit(-1)
    src_left = stitched[:, 0:stitched.shape[1] - SHRT_MAX]
    stitched = stitched[:, stitched.shape[1] - SHRT_MAX:stitched.shape[1]]
    parts.append(src_left)
parts.append(stitched)

print("[INFO] correct...")

orig1 = parts[0].copy()
pts1 = four_point_get(parts[0])
warped1 = four_point_transform(orig1, pts1)
if len(parts) > 1:
    orig2 = parts[1].copy()
    pts2 = four_point_get(parts[1])
    warped2 = four_point_transform(orig2, pts2)
    warped = cv2.hconcat([warped1, warped2])
else:
    warped = warped1
if is_show:
    print("展示校正结果")
    cv2.namedWindow(os.path.basename(src_FILE), cv2.WINDOW_FREERATIO)
    cv2.imshow(os.path.basename(src_FILE), warped)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
if args["output"] is not None:
    cv2.imwrite(args["output"],warped)
# # 抓取具有最大区域的轮廓（即拼接图像本身的轮廓），cv2.contourArea是求轮廓面积
# c = max(cnts, key=cv2.contourArea)
# print(c)
# print(c.shape)
#
# # 计算出最大轮廓的边界框,使用边界矩形信息.使用 cv2.boundingRect(img) 函数，用一个最小的矩形，
# # 把找到的形状包起来，img是一个二值图，也就是它的参数；返回四个值，分别是 x，y，w，h；
# # 其中 x，y 是矩阵左上点的坐标，w，h 是矩阵的宽和高
# (x, y, w, h) = cv2.boundingRect(c)
# print((x, y, w, h))
